package rest

import (
	"net/http"

	"github.com/labstack/echo/v4"
	"github.com/pkg/errors"

	pb "ldaccount/api/pb/server"
	"ldaccount/internal/object"
	"ldaccount/internal/service"
	"ldaccount/pkg/lueerror"
)

type accHandler struct {
	*Base
	Name string
}

func NewAccountHandler() *accHandler {
	return &accHandler{
		Base: base,
		Name: "accounts_handler",
	}
}

// RegisterHandler 注册路由
func (ah *accHandler) RegisterHandler(r *echo.Group) {
	r.GET("/v1/hello-world", ah.helloWorld)
	// 账号导入
	r.POST("/v1/accounts/import", ah.accountImport)
	// 账号注册
	r.POST("/v1/register", ah.registerAccount)
	// 账号登录
	r.POST("/v1/login", ah.loginAccount)
	// 账号信息
	r.GET("/v1/accounts/info", ah.accountInfo)
	// 更新账号信息
	r.PUT("/v1/accounts/info", ah.updateAccount)
	// 修改账号密码
	r.PUT("/v1/accounts/password", ah.resetPassword)
	// 检查手机号
	r.GET("/v1/accounts/mobile", ah.checkMobile)
	// 根据gid 获取用户信息
	r.GET("/v1/accounts/gid", ah.getAccByGID)
	// 发送短信验证码
	r.POST("/v1/mobile/code/send", ah.sendMobileCode)
	// 短信验证
	r.POST("/v1/mobile/code/verify", ah.verifyMobileCode)
	// 获取登陆二维码
	r.GET("/v1/mp/qrcode", ah.getMpQrCode)
	// 根据 ticket 登录
	r.GET("/v1/login/:ticket", ah.loginWithTicket)
	// 根据 ticket 注册
	r.POST("/v1/ticket/register", ah.registerWithTicket)
}

func (ah *accHandler) helloWorld(c echo.Context) error {
	tag := c.QueryParam("tag")
	if tag == "panic" {
		panic("test panic")
	} else if tag == "lueerror" {
		return lueerror.ServerError(errors.New("test"), "TEST_ERROR")
	} else if tag == "error" {
		return errors.New("raw error")
	} else if tag == "echoerror" {
		return echo.ErrBadRequest
	}
	return c.JSON(http.StatusOK, map[string]interface{}{"Detail": "hello world!"})
}

func (ah *accHandler) originReqCheck(mobile, email, codeOrigin string) lueerror.LueError {
	if !ContainsStr([]string{"mobile", "email"}, codeOrigin) {
		return lueerror.InvalidRequest(errors.New("code_origin 错误"))
	}
	if codeOrigin == "mobile" && mobile == "" {
		return lueerror.InvalidMobile(errors.New("手机号不为空"))
	} else if codeOrigin == "email" && email == "" {
		return lueerror.InvalidMobile(errors.New("邮箱不为空"))
	}
	return nil
}

func (ah *accHandler) accountImport(c echo.Context) error {
	ctx := c.Request().Context()
	type param struct {
		Origin   string              `json:"origin" validate:"required"`
		Accounts []*object.AccImport `json:"accounts" validate:"gt=0,dive,required"`
	}
	req := &param{}
	if errL := ah.ParseBody(c, req); errL != nil {
		return errL
	}
	num, errL := service.GetAccountsSrv().ImportAccounts(ctx, req.Accounts, req.Origin)
	return ah.SimpleRet(c, map[string]interface{}{"import_num": num}, errL)
}

func (ah *accHandler) registerAccount(c echo.Context) error {
	ctx := c.Request().Context()
	type param struct {
		Mobile      string `json:"mobile"`
		Email       string `json:"email"`
		UserName    string `json:"user_name"`
		IsMd5       bool   `json:"is_md5"` // 密码是否已经md5加密
		Password    string `json:"password"`
		Code        string `json:"code" validate:"required"` // 手机验证码
		CodeType    string `json:"code_type"`                // 短信类型
		CountryCode string `json:"country_code"`             // 国家码
		Origin      string `json:"origin"`
		CodeOrigin  string `json:"code_origin"` // code 来源
	}
	req := &param{}
	if errL := ah.ParseBody(c, req); errL != nil {
		return errL
	}
	if req.CodeOrigin == "" {
		req.CodeOrigin = "mobile"
	}
	if req.Mobile == "" && req.Email == "" {
		return lueerror.InvalidRequest(errors.New("mobile 或 email 不能都为空"))
	}
	if errL := ah.EmailMobileCheck(req.Email, req.Mobile); errL != nil {
		return errL
	}
	if errL := ah.originReqCheck(req.Mobile, req.Email, req.CodeOrigin); errL != nil {
		return errL
	}
	if req.Password == "" {
		req.Password = "123456"
		req.IsMd5 = false
	}
	if req.CountryCode == "" {
		req.CountryCode = "86"
	}

	ret, errL := service.GetAccountsSrv().RegisterAccount(ctx, req.Mobile, req.Email, req.UserName, req.Password,
		req.Code, req.CodeType, req.CountryCode, req.CodeOrigin, req.Origin, req.IsMd5)
	return ah.SimpleRet(c, ret, errL)
}

func (ah *accHandler) loginAccount(c echo.Context) error {
	ctx := c.Request().Context()
	type param struct {
		Mobile     string `json:"mobile"`
		Email      string `json:"email"`
		Password   string `json:"password"`
		IsMD5      bool   `json:"is_md5"`
		Code       string `json:"code"`
		CodeType   string `json:"code_type"`
		CodeOrigin string `json:"code_origin"`
	}
	req := &param{}
	if err := ah.ParseBody(c, req); err != nil {
		return lueerror.InvalidRequest(err)
	}
	if req.Mobile == "" && req.Email == "" {
		return lueerror.InvalidRequest(errors.New("mobile 或 email 不能都为空"))
	}
	if req.Code == "" && req.Password == "" {
		return lueerror.InvalidRequest(errors.New("请输入密码或验证码"))
	}
	if req.CodeOrigin == "" {
		req.CodeOrigin = "mobile"
	}
	if errL := ah.originReqCheck(req.Mobile, req.Email, req.CodeOrigin); errL != nil {
		return errL
	}
	if errL := ah.EmailMobileCheck(req.Email, req.Mobile); errL != nil {
		return errL
	}
	ret, errL := service.GetAccountsSrv().AccountLogin(ctx, req.Mobile, req.Email, req.Password, req.Code,
		req.CodeType, req.CodeOrigin, req.IsMD5)
	if errL != nil {
		return errL
	}
	ret.Token, errL = service.GetAccountsSrv().GenJwtToken(ret.GID, ret.Mobile, ret.Email, ret.UserName)
	return ah.SimpleRet(c, ret, errL)
}

func (ah *accHandler) accountInfo(c echo.Context) error {
	ctx := c.Request().Context()
	token, errL := ah.VerifyJwtToken(c)
	if errL != nil {
		return errL
	}
	ret, errL := service.GetAccountsSrv().GetAccByGID(ctx, token.GID)
	return ah.SimpleRet(c, ret, errL)
}

// 更新用户信息
func (ah *accHandler) updateAccount(c echo.Context) error {
	ctx := c.Request().Context()
	token, errL := ah.VerifyJwtToken(c)
	if errL != nil {
		return errL
	}
	req := &pb.UpdateAccountReqV2{}
	if errL = ah.ParseBody(c, req); errL != nil {
		return errL
	}

	if errL = ah.EmailMobileCheck(req.Email, req.Mobile); errL != nil {
		return errL
	}
	if req.Code != "" {
		if req.CodeOrigin == "" {
			req.CodeOrigin = "mobile"
		}
		if errL = ah.originReqCheck(req.Mobile, req.Email, req.CodeOrigin); errL != nil {
			return errL
		}
	}
	ret, errL := service.GetAccountsSrv().UpdateAcc(ctx, token.GID, req.Mobile, req.Email, req.UserName,
		req.Avatar, req.CountryCode, req.Code, req.CodeType, req.CodeOrigin)
	return ah.SimpleRet(c, ret, errL)
}

func (ah *accHandler) resetPassword(c echo.Context) error {
	ctx := c.Request().Context()
	type param struct {
		Mobile     string `json:"mobile"`
		Email      string `json:"email"`
		Password   string `json:"password" validate:"required"`
		OldPwd     string `json:"old_pwd"`
		IsMD5      bool   `json:"is_md5"`
		Code       string `json:"code"`
		CodeType   string `json:"code_type"`
		CodeOrigin string `json:"code_origin"`
	}
	req := &param{}
	var errL lueerror.LueError
	if errL = ah.ParseBody(c, req); errL != nil {
		return errL
	}
	// 根据验证码修改
	if req.Code != "" {
		if req.CodeOrigin == "" {
			req.CodeOrigin = "mobile"
		}
		if errL = ah.originReqCheck(req.Mobile, req.Email, req.CodeOrigin); errL != nil {
			return errL
		}
		if errL = ah.EmailMobileCheck(req.Email, req.Mobile); errL != nil {
			return errL
		}
		errL = service.GetAccountsSrv().ResetPasswordByCode(ctx, req.Mobile, req.Email, req.Password, req.Code,
			req.CodeType, req.CodeOrigin, req.IsMD5)
		return ah.SimpleRet(c, nil, errL)
	} else {
		// 根据旧密码修改
		token, errL := ah.VerifyJwtToken(c)
		if errL != nil {
			return errL
		}
		if req.OldPwd == "" {
			return lueerror.InvalidRequest(errors.New("旧密码不为空"))
		}
		errL = service.GetAccountsSrv().ResetPasswordByOld(ctx, token.GID, req.Password, req.OldPwd, req.IsMD5)
		return ah.SimpleRet(c, nil, errL)
	}
}

func (ah *accHandler) checkMobile(c echo.Context) error {
	ctx := c.Request().Context()
	mobile := c.QueryParams().Get("mobile")
	if errL := ah.MobileCheck(mobile); errL != nil {
		return errL
	}
	ret, errL := service.GetAccountsSrv().CheckMobile(ctx, mobile)
	return ah.SimpleRet(c, ret, errL)
}

func (ah *accHandler) getAccByGID(c echo.Context) error {
	ctx := c.Request().Context()
	gID := c.QueryParams().Get("g_id")
	ret, errL := service.GetAccountsSrv().GetAccByGID(ctx, gID)
	return ah.SimpleRet(c, ret, errL)
}

func (ah *accHandler) sendMobileCode(c echo.Context) error {
	ctx := c.Request().Context()
	type param struct {
		Mobile      string `json:"mobile" validate:"required"`
		CodeType    string `json:"code_type" validate:"required"`
		CountryCode string `json:"country_code"`
	}
	req := &param{}
	if errL := ah.ParseBody(c, req); errL != nil {
		return errL
	}
	if errL := ah.MobileCheck(req.Mobile); errL != nil {
		return errL
	}
	errL := service.GetAccountsSrv().SendMobileCode(ctx, req.Mobile, req.CodeType)
	return ah.SimpleRet(c, nil, errL)
}

func (ah *accHandler) verifyMobileCode(c echo.Context) error {
	ctx := c.Request().Context()
	type param struct {
		Mobile   string `json:"mobile" validate:"required"`
		CodeType string `json:"code_type" validate:"required"`
		Code     string `json:"code" validate:"required"`
	}
	req := &param{}
	if errL := ah.ParseBody(c, req); errL != nil {
		return errL
	}
	errL := service.GetAccountsSrv().VerifyMobileCode(ctx, req.Mobile, req.Code, req.CodeType)
	return ah.SimpleRet(c, nil, errL)
}

func (ah *accHandler) getMpQrCode(c echo.Context) error {
	ctx := c.Request().Context()
	ret, errL := service.GetAccountsSrv().GetLoginQrCode(ctx, ah.cfg.Biz.MpAppId)
	return ah.SimpleRet(c, ret, errL)
}

func toLoginAccountInfoResp(info *object.LoginAccInfo) *pb.AccountChannelInfoResp {
	return &pb.AccountChannelInfoResp{
		GId:      info.GID,
		Mobile:   info.Mobile,
		Email:    info.Email,
		UserName: info.UserName,
		Status:   info.Status,
		AppId:    info.AppId,
		Platform: info.Platform,
		OpenId:   info.OpenId,
		Avatar:   info.Avatar,
		Token:    "",
	}
}

func (ah *accHandler) loginWithTicket(c echo.Context) error {
	ctx := c.Request().Context()
	rId := c.Param("ticket")
	accInfo, errL := service.GetAccountsSrv().LoginWithTicket(ctx, rId)
	if errL != nil {
		return errL
	}
	ret := toLoginAccountInfoResp(accInfo)
	if ret.GId != "" {
		ret.Token, errL = service.GetService().GenJwtToken(ret.GId, ret.Mobile, ret.Email, ret.UserName)
	}
	return ah.SimpleRet(c, ret, errL)
}

func (ah *accHandler) registerWithTicket(c echo.Context) error {
	ctx := c.Request().Context()
	req := &pb.AccountRegWithMp{}
	if errL := ah.ParseBody(c, req); errL != nil {
		return errL
	}
	// 没有密码，验证码必填
	if req.Password == "" && req.Code == "" {
		return lueerror.InvalidRequest(errors.New("验证码必填"))
	}
	if req.CodeOrigin == "" {
		req.CodeOrigin = "mobile"
	}
	// 格式检查
	if errL := ah.originReqCheck(req.Mobile, req.Email, req.CodeOrigin); errL != nil {
		return errL
	}
	if errL := ah.EmailMobileCheck(req.Email, req.Mobile); errL != nil {
		return errL
	}
	// 注册
	info, errL := service.GetAccountsSrv().RegisterWithTicket(ctx, req.Ticket, req.Mobile, req.Email, req.Password,
		req.Code, req.CodeType, "86", req.CodeOrigin, req.IsMd5)
	if errL != nil {
		return errL
	}
	ret := toLoginAccountInfoResp(info)
	return ah.SimpleRet(c, ret, errL)
}
